package com.njbw.rateLimit;

import lombok.Data;

import java.util.LinkedList;
import java.util.Random;

/**
 * 滑动窗口
 *
 * @author 黄侯淳
 * @since JDK 1.8
 */
public class MovingWindow {

    // 时间窗口/,s
    private final int interval = 1000;

    // 时间窗口内最大访问次数
    private final int limit = 5;

    // 分割窗口个数
    private int slotCount = 5;

    private LinkedList<Node> slot = new LinkedList<>();

    public MovingWindow() {
        // 启动一个线程，每 200 毫秒，将窗口向前移动一格（类似于守护线程）
        new Thread(() -> {
            while (true) {
                // 每过 200 毫秒，就将窗口向前移动一格
                if (slot.size() == slotCount) {
                    slot.poll();
                }
                slot.offer(new Node(System.currentTimeMillis()));
                try {
                    Thread.sleep(interval / slotCount);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public boolean tryAcquire() {
        Node currWindow = getCurrWindow();
        currWindow.setCount(currWindow.getCount() + 1);
        return getCounter() <= limit;
    }

    private int getCounter() {
        return slot.stream().mapToInt(Node::getCount).sum();
    }

    private Node getCurrWindow() {
        if (slot.isEmpty()) {
            while (true) {
                if (slot.isEmpty()) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    break;
                }
            }
        }
        return slot.getLast();
    }

    @Data
    private class Node {
        private long time;
        private int count;

        public Node(long time) {
            this.time = time;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MovingWindow counter = new MovingWindow();
        while (true) {
            counter.slot.stream().forEach(node -> System.out.print(node.getTime() + ":" + node.getCount() + "|"));
            if (counter.tryAcquire()) {
                System.out.println("请求成功...");
            } else {
                System.out.println("限流了...");
            }
            Thread.sleep(100 * new Random().nextInt(5));
        }
    }
}
